#include <stdio.h>
#include <string.h>
#include <time.h>

#include "smart_str.h"

void _smart_str_init(smart_str_t *ss)
{
	pthread_mutex_init(&ss->lock, NULL);
	pthread_cond_init(&ss->cond, NULL);
}

void _smart_str_init_ex(smart_str_t *ss, uint8_t *buf, uint32_t size)
{
	memset(ss, 0, sizeof(smart_str_t));
	ss->buf = buf;
	ss->size = size;
	_smart_str_init(ss);
}

void _smart_str_clear(smart_str_t *ss)
{
	pthread_mutex_lock(&ss->lock);
	ss->rpos = 0;
	ss->tpos = 0;
	ss->rx_size = 0;
	ss->rx_skip = 0;
	pthread_mutex_unlock(&ss->lock);
}

void _smart_str_destroy(smart_str_t *ss)
{
	pthread_mutex_destroy(&ss->lock);
	pthread_cond_destroy(&ss->cond);
}

void _smart_str_signal(smart_str_t *ss)
{
	pthread_mutex_lock(&ss->lock);
	if(ss->rpos != ss->tpos) pthread_cond_signal(&ss->cond);
	pthread_mutex_unlock(&ss->lock);
}

uint32_t _smart_str_put_size(smart_str_t *ss)
{
	uint32_t remain;

	pthread_mutex_lock(&ss->lock);
	if(ss->rpos > ss->tpos) {
		remain = ss->rpos - ss->tpos;
	} else {
		remain = ss->size - ss->tpos + ss->rpos;
	}
	pthread_mutex_unlock(&ss->lock);

	return remain;
}

bool _smart_str_put(smart_str_t *ss, const uint8_t *buf, uint32_t len) {
	uint32_t i, remain, sz;

	if(!len)
		return false;

	pthread_mutex_lock(&ss->lock);

	if(ss->rpos > ss->tpos) {
		remain = ss->rpos - ss->tpos;
	} else {
		remain = ss->size - ss->tpos + ss->rpos;
	}

	if(remain <= len)
	{
		pthread_mutex_unlock(&ss->lock);
		return false;
	}

	sz = ss->size - ss->tpos;
	if(sz < len) {
		memcpy(ss->buf + ss->tpos, buf, sz);
		memcpy(ss->buf, buf + sz, len - sz);
		ss->tpos = len - sz;
	} else {
		memcpy(ss->buf + ss->tpos, buf, len);

		i = ss->tpos + len;
		if(i == ss->size) i = 0;
		ss->tpos = i;
	}

	pthread_mutex_unlock(&ss->lock);

	return true;
}

uint32_t _smart_str_get_size(smart_str_t *ss)
{
	uint32_t written;

	pthread_mutex_lock(&ss->lock);
	if(ss->tpos == ss->rpos)
	{
		written = 0;
	}
	else if(ss->tpos > ss->rpos) {
		written = ss->tpos - ss->rpos;
	} else {
		written = ss->size - ss->rpos + ss->tpos;
	}
	pthread_mutex_unlock(&ss->lock);

	return written;
}

uint32_t _smart_str_get(smart_str_t *ss, uint8_t *buf, uint32_t len, uint32_t msec) {
	uint32_t i, written, sz;
	if(!len)
		return 0;

	pthread_mutex_lock(&ss->lock);

	if(ss->rpos == ss->tpos)
	{
		if(msec)
		{
			struct timespec ts;

			if(!clock_gettime(CLOCK_REALTIME, &ts))
			{
				uint64_t ns = ts.tv_nsec + msec * 1000000;

				ts.tv_sec += (ns / 1000000000);
				ts.tv_nsec = ns % 1000000000;
				
				if(!pthread_cond_timedwait(&ss->cond, &ss->lock, &ts))
				{
					goto next;
				}
			}
		}
		pthread_mutex_unlock(&ss->lock);
		return 0;
	}

next:
	if(ss->tpos > ss->rpos) {
		written = ss->tpos - ss->rpos;
	} else {
		written = ss->size - ss->rpos + ss->tpos;
	}

	if(len > written)
		len = written;

	sz = ss->size - ss->rpos;
	if(sz < len) {
		memcpy(buf, ss->buf + ss->rpos, sz);
		memcpy(buf + sz, ss->buf, len - sz);
		ss->rpos = len - sz;
	} else {
		memcpy(buf, ss->buf + ss->rpos, len);

		i = ss->rpos + len;
		if(i == ss->size) i = 0;
		ss->rpos = i;
	}

	pthread_mutex_unlock(&ss->lock);

	return len;
}

bool _smart_str_readline(smart_str_t *ss, char *buf, uint32_t len) {
	char chr;
	while(_smart_str_get(ss, (uint8_t*) &chr, 1, 0)) {
		if(chr == '\n') {
			if(ss->rx_skip) {
				ss->rx_skip = 0;
				ss->rx_size = 0;
				continue;
			}
			buf[ss->rx_size] = '\0';

			if(ss->rx_size) {
				char *p = buf, *p2;
				while(*p && (*p == ' ' || *p == '\t' || *p == '\r'))
					p++;

				if(p - buf) {
					p2 = buf;
					while(*p)
						*p2++ = *p++;
					*p2-- = '\0';
				} else {
					p2 = buf + ss->rx_size - 1;
				}

				p = buf;
				while(p2 > p && (*p2 == ' ' || *p2 == '\t' || *p2 == '\r'))
					*p2-- = '\0';

			}

			ss->rx_size = 0;

			return true;
		} else if(ss->rx_size < len) {
			buf[ss->rx_size++] = chr;
		} else {
			ss->rx_skip = 1;
		}
	}
	return false;
}

void _smart_str_vprintf(smart_str_t *ss, char *buf, size_t len, const char *fmt, va_list ap) {
	int n = vsnprintf(buf, len, fmt, ap);
	if(n > 0 && n < (int) len) {
		_smart_str_put(ss, (uint8_t*) buf, n);
	}
}

void _smart_str_sprintf(smart_str_t *ss, char *buf, size_t len, const char *fmt, ...) {
	va_list ap;

	va_start(ap, fmt);
	_smart_str_vprintf(ss, buf, len, fmt, ap);
	va_end(ap);
}

void _smart_str_aprintf(smart_str_t *ss, const char *fmt, ...) {
	char buf[256];
	va_list ap;

	va_start(ap, fmt);
	_smart_str_vprintf(ss, buf, sizeof(buf), fmt, ap);
	va_end(ap);
}

void _smart_str_aprintf2(smart_str_t *ss, const char *fmt, ...) {
	char buf[512];
	va_list ap;

	va_start(ap, fmt);
	_smart_str_vprintf(ss, buf, sizeof(buf), fmt, ap);
	va_end(ap);
}

void _smart_str_aprintf4(smart_str_t *ss, const char *fmt, ...) {
	char buf[1024];
	va_list ap;

	va_start(ap, fmt);
	_smart_str_vprintf(ss, buf, sizeof(buf), fmt, ap);
	va_end(ap);
}
